/* Copyright(C) 2006-2008 Dave Sexton  *
 * http://www.codeplex.com/aip         *
 * http://www.codeplex.com/aip/license *
 ***************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Drawing;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Web.UI.Design.WebControls;
using System.Web.UI.Design;
using System.ComponentModel.Design;
using DaveSexton.Web.Controls.Design;
using DaveSexton.Web.Controls.Resources;

namespace DaveSexton.Web.Controls
{
	/// <summary>
	/// Provides user interface (UI) elements for Auto-Input Protection (AIP).
	/// </summary>
	/// <include file='comments/AutoInputProtectionControl.xml' path='//comments/remarks'/>
	/// <include file='comments/autoinputprotectioncontrol.xml' path='//comments/example[@id="screenshots"]'/>
	/// <include file='comments/autoinputprotectioncontrol.xml' path='//comments/example[@id=1]'/>
	/// <include file='comments/autoinputprotectioncontrol.xml' path='//comments/example[@id=2]'/>
	[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2112:SecuredTypesShouldNotExposeFields")]
	[DefaultProperty("RequestKeepAlive"), DefaultEvent("UserValidated")]
	[ToolboxData("<{0}:AutoInputProtectionControl runat=server></{0}:AutoInputProtectionControl>")]
	[Designer(typeof(AutoInputProtectionControlDesigner))]
	[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal), 
	AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
	public class AutoInputProtectionControl : CompositeControl
	{
		#region Public Properties
		/// <summary>
		/// The ID of an optional <see cref="ITextControl"/> control that this control looks for when a custom <see cref="Template"/> is defined.
		/// </summary>
		public static readonly string TextBoxId = "Text";
		/// <summary>
		/// The ID of a required <see cref="System.Web.UI.WebControls.Image"/> control that this control looks for when a custom <see cref="Template"/> is defined.
		/// </summary>
		public static readonly string ImageId = "Image";
		/// <summary>
		/// The ID of an optional <see cref="ITextControl"/> control that this control looks for when a custom <see cref="Template"/> is defined.
		/// </summary>
		public static readonly string LabelId = "Label";
		/// <summary>
		/// The ID of an optional <see cref="ITextControl"/> control that this control looks for when a custom <see cref="Template"/> is defined.
		/// </summary>
		public static readonly string InstructionId = "Instruction";
		/// <summary>
		/// The ID of an optional <see cref="ITextControl"/> control that this control looks for when a custom <see cref="Template"/> is defined.
		/// </summary>
		public static readonly string TitleId = "Title";
		/// <summary>
		/// The ID of an optional <see cref="IValidator"/> control that this control looks for when a custom <see cref="Template"/> is defined.
		/// </summary>
		public static readonly string RequiredValidatorId = "RequiredValidator";
		/// <summary>
		/// The ID of a required <see cref="CustomValidator"/> control that this control looks for when a custom <see cref="Template"/> is defined.
		/// </summary>
		public static readonly string ValidatorId = "Validator";
		/// <summary>
		/// The default dimensions of the image.
		/// </summary>
		public static readonly Size DefaultImageSize = new Size(150, 50);

		#region Appearance
		/// <summary>
		/// Gets or sets the label of the input control.
		/// </summary>
		/// <value>Input label.</value>
		[Category("Appearance"), PropertyDescription("LabelText"), Localizable(true)]
		public string LabelText
		{
			get
			{
				return ViewState["LabelText"] as string ?? Resources.Text.AIP_LabelText;
			}
			set
			{
				ViewState["LabelText"] = value;
			}
		}

		private bool ShouldSerializeLabelText()
		{
			return !LabelText.Equals(Resources.Text.AIP_LabelText, StringComparison.Ordinal);
		}

		private void ResetLabelText()
		{
			LabelText = Resources.Text.AIP_LabelText;
		}

		/// <summary>
		/// Gets or sets the text that is displayed in <see cref="ValidationSummary"/> controls when the form is submitted with an empty value in the input control.
		/// </summary>
		/// <value>Error message displayed to the user to indicate that input is required.</value>
		[Category("Appearance"), PropertyDescription("RequiredErrorMessage"), Localizable(true)]
		public string RequiredErrorMessage
		{
			get
			{
				return ViewState["RequiredErrorMessage"] as string ?? Resources.Text.AIP_RequiredErrorMessage;
			}
			set
			{
				ViewState["RequiredErrorMessage"] = value;
			}
		}

		private bool ShouldSerializeRequiredErrorMessage()
		{
			return !RequiredErrorMessage.Equals(Resources.Text.AIP_RequiredErrorMessage, StringComparison.Ordinal);
		}

		private void ResetRequiredErrorMessage()
		{
			RequiredErrorMessage = Resources.Text.AIP_RequiredErrorMessage;
		}

		/// <summary>
		/// Gets or sets the text that is displayed in <see cref="ValidationSummary"/> controls when the form is submitted with input that does not match the text on the image.
		/// </summary>
		/// <value>Error message displayed to the user to indicate that the text they have entered is invalid.</value>
		[Category("Appearance"), PropertyDescription("InvalidErrorMessage"), Localizable(true)]
		public string InvalidErrorMessage
		{
			get
			{
				return ViewState["InvalidErrorMessage"] as string ?? Resources.Text.AIP_InvalidErrorMessage;
			}
			set
			{
				ViewState["InvalidErrorMessage"] = value;
			}
		}

		private bool ShouldSerializeInvalidErrorMessage()
		{
			return !InvalidErrorMessage.Equals(Resources.Text.AIP_InvalidErrorMessage, StringComparison.Ordinal);
		}

		private void ResetInvalidErrorMessage()
		{
			InvalidErrorMessage = Resources.Text.AIP_InvalidErrorMessage;
		}

		/// <summary>
		/// Gets or sets the text that is displayed inline when the form is submitted with an empty value in the input control.
		/// </summary>
		/// <value>Error message displayed to the user to indicate that input is required.</value>
		[Category("Appearance"), PropertyDescription("RequiredErrorText"), Localizable(true)]
		public string RequiredErrorText
		{
			get
			{
				return ViewState["RequiredErrorText"] as string ?? Resources.Text.AIP_RequiredErrorText;
			}
			set
			{
				ViewState["RequiredErrorText"] = value;
			}
		}

		private bool ShouldSerializeRequiredErrorText()
		{
			return !RequiredErrorText.Equals(Resources.Text.AIP_RequiredErrorText, StringComparison.Ordinal);
		}

		private void ResetRequiredErrorText()
		{
			RequiredErrorText = Resources.Text.AIP_RequiredErrorText;
		}

		/// <summary>
		/// Gets or sets the text that is displayed inline when the form is submitted with input that does not match the text on the image.
		/// </summary>
		/// <value>Error message displayed to the user to indicate that the text they have entered is invalid.</value>
		[Category("Appearance"), PropertyDescription("InvalidErrorText"), Localizable(true)]
		public string InvalidErrorText
		{
			get
			{
				return ViewState["InvalidErrorText"] as string ?? Resources.Text.AIP_InvalidErrorText;
			}
			set
			{
				ViewState["InvalidErrorText"] = value;
			}
		}

		private bool ShouldSerializeInvalidErrorText()
		{
			return !InvalidErrorText.Equals(Resources.Text.AIP_InvalidErrorText, StringComparison.Ordinal);
		}

		private void ResetInvalidErrorText()
		{
			InvalidErrorText = Resources.Text.AIP_InvalidErrorText;
		}

		/// <summary>
		/// Gets or sets the title text.
		/// </summary>
		/// <value>Title text.</value>
		[Category("Appearance"), PropertyDescription("TitleText"), Localizable(true)]
		public string TitleText
		{
			get
			{
				return ViewState["TitleText"] as string ?? Resources.Text.AIP_TitleText;
			}
			set
			{
				ViewState["TitleText"] = value;
			}
		}

		private bool ShouldSerializeTitleText()
		{
			return !TitleText.Equals(Resources.Text.AIP_TitleText, StringComparison.Ordinal);
		}

		private void ResetTitleText()
		{
			TitleText = Resources.Text.AIP_TitleText;
		}

		/// <summary>
		/// Gets or sets the instructional text that is displayed to the user.
		/// </summary>
		/// <value>Instructional text that is displayed to the user.</value>
		[Category("Appearance"), PropertyDescription("InstructionText"), Localizable(true)]
		public string InstructionText
		{
			get
			{
				return ViewState["InstructionText"] as string ?? DefaultInstructionText;
			}
			set
			{
				ViewState["InstructionText"] = value;
			}
		}

		private bool ShouldSerializeInstructionText()
		{
			return !InstructionText.Equals(DefaultInstructionText, StringComparison.Ordinal);
		}

		private void ResetInstructionText()
		{
			InstructionText = DefaultInstructionText;
		}
		#endregion

		#region Behavior
		/// <summary>
		/// Gets or sets whether the <see cref="RequestTimeout">RequestTimeout</see> property specifies an absolute
		/// expiration or a sliding expiration and whether multiple requests from a client for the same image may
		/// succeeed before validation occurs.
		/// </summary>
		/// <!--remarks:--><include file="comments/autoinputprotectioncontrol.xml" path='//property[@name="RequestKeepAlive"]/*'/>
		/// <seealso cref="RequestTimeout"/>
		/// <value>Indicates whether the image should remain on the server so that it may be retrieved multiple times.</value>
		[Category("Behavior"), DefaultValue(false), PropertyDescription("RequestKeepAlive")]
		public bool RequestKeepAlive
		{
			get
			{
				return ((bool?) ViewState["RequestKeepAlive"]).GetValueOrDefault(false);
			}
			set
			{
				ViewState["RequestKeepAlive"] = value;
			}
		}

		/// <summary>
		/// Gets or sets the amount of time, in seconds, that may pass before an image will be removed from the server so that it 
		/// can no longer be used.
		/// </summary>
		/// <!--remarks:--><include file="comments/autoinputprotectioncontrol.xml" path='//property[@name="RequestTimeout"]/*'/>
		/// <seealso cref="RequestKeepAlive"/>
		/// <value>The number of seconds that may pass before an image will be removed from the server so that it can no longer be used.</value>
		[Category("Behavior"), DefaultValue(15), PropertyDescription("RequestTimeout")]
		public int RequestTimeout
		{
			get
			{
				return ((int?) ViewState["RequestTimeout"]).GetValueOrDefault(15);
			}
			set
			{
				ViewState["RequestTimeout"] = value;
			}
		}

		/// <summary>
		/// Gets or sets whether the <see cref="ValidationTimeout">ValidationTimeout</see> property is ignored.
		/// </summary>
		/// <!--remarks:--><include file="comments/autoinputprotectioncontrol.xml" path='//property[@name="ValidationKeepAlive"]/*'/>
		/// <seealso cref="ValidationTimeout"/>
		/// <value>Indicates whether tests will expire.</value>
		[Category("Behavior"), DefaultValue(false), PropertyDescription("ValidationKeepAlive")]
		public bool ValidationKeepAlive
		{
			get
			{
				return ((bool?) ViewState["ValidationKeepAlive"]).GetValueOrDefault(false);
			}
			set
			{
				ViewState["ValidationKeepAlive"] = value;
			}
		}

		/// <summary>
		/// Gets or sets the amount of time, in seconds, that may pass before a challenge's text will be removed from 
		/// the server so that it can no longer be used to validate requests.
		/// </summary>
		/// <!--remarks:--><include file="comments/autoinputprotectioncontrol.xml" path='//property[@name="ValidationTimeout"]/*'/>
		/// <seealso cref="ValidationKeepAlive"/>
		/// <value>The number of seconds that may pass before a test will be removed from the server so that it can no longer be used
		/// to validate requests.</value>
		[Category("Behavior"), DefaultValue(30), PropertyDescription("ValidationTimeout")]
		public int ValidationTimeout
		{
			get
			{
				return ((int?) ViewState["ValidationTimeout"]).GetValueOrDefault(30);
			}
			set
			{
				ViewState["ValidationTimeout"] = value;
			}
		}

		/// <summary>
		/// Gets or sets the name of the validation group to which the validator controls are assigned.
		/// </summary>
		/// <value>The name of the validation group to which the required-field and custom validators are assigned.</value>
		[Category("Behavior"), DefaultValue(""), PropertyDescription("ValidationGroup")]
		public string ValidationGroup
		{
			get
			{
				return ViewState["ValidationGroup"] as string ?? string.Empty;
			}
			set
			{
				ViewState["ValidationGroup"] = value;
			}
		}

		/// <summary>
		/// Gets or sets a value that indicates whether the input field will be focused automatically when the text that is 
		/// submitted does not match the text on the image.
		/// </summary>
		/// <remarks>
		/// When <see langword="true"/>, this propery does not focus the composite <see cref="AutoInputProtectionControl"/> itself.
		/// Instead, focus is applied to the input control in the default template; in a custom template focus is applied to the 
		/// control being validated by the <see cref="CustomValidator"/> control that has an ID of <em>Validator</em>.
		/// </remarks>
		/// <value>Indicates whether the input field will be focused automatically when validation fails.</value>
		[Category("Behavior"), DefaultValue(true), PropertyDescription("SetFocusOnFailedValidation")]
		public bool SetFocusOnFailedValidation
		{
			get
			{
				return ((bool?) ViewState["SetFocusOnFailedValidation"]).GetValueOrDefault(true);
			}
			set
			{
				ViewState["SetFocusOnFailedValidation"] = value;
			}
		}
		#endregion

		#region Styles
		/// <summary>
		/// Gets a reference to a <see cref="Style"/> object that defines the appearance of the <see cref="System.Web.UI.WebControls.Image"/> control.
		/// </summary>
		/// <remarks>
		/// This property returns a <see cref="Style"/> object that defines the appearance of the control that has an ID of 
		/// <strong>Image</strong> in a custom <see cref="Template"/> and the default image element when a custom template is not 
		/// defined.
		/// </remarks>
		[Category("Styles"), DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		PersistenceMode(PersistenceMode.InnerProperty), NotifyParentProperty(true), PropertyDescription("ImageStyle")]
		public Style ImageStyle
		{
			get
			{
				if (imageStyle == null)
				{
					imageStyle = new Style();

					if (IsTrackingViewState)
						((IStateManager) imageStyle).TrackViewState();
				}

				return imageStyle;
			}
		}

		/// <summary>
		/// Gets a reference to a <see cref="Style"/> object that defines the appearance of the input field.
		/// </summary>
		/// <remarks>
		/// This property returns a <see cref="Style"/> object that defines the appearance of the control that has an ID of 
		/// <strong>Text</strong> in a custom <see cref="Template"/> and the default input field when a custom template is not 
		/// defined.
		/// </remarks>
		[Category("Styles"), DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		PersistenceMode(PersistenceMode.InnerProperty), NotifyParentProperty(true), PropertyDescription("TextBoxStyle")]
		public Style TextBoxStyle
		{
			get
			{
				if (textBoxStyle == null)
				{
					textBoxStyle = new Style();

					if (IsTrackingViewState)
						((IStateManager) textBoxStyle).TrackViewState();
				}

				return textBoxStyle;
			}
		}

		/// <summary>
		/// Gets a reference to a <see cref="Style"/> object that defines the appearance of the title text.
		/// </summary>
		/// <remarks>
		/// This property returns a <see cref="Style"/> object that defines the appearance of the control that has an ID of 
		/// <strong>Title</strong> in a custom <see cref="Template"/> and the default title when a custom template is not 
		/// defined.
		/// </remarks>
		[Category("Styles"), DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		PersistenceMode(PersistenceMode.InnerProperty), NotifyParentProperty(true), PropertyDescription("TitleTextStyle")]
		public Style TitleTextStyle
		{
			get
			{
				if (titleTextStyle == null)
				{
					titleTextStyle = new Style();

					if (IsTrackingViewState)
						((IStateManager) titleTextStyle).TrackViewState();
				}

				return titleTextStyle;
			}
		}

		/// <summary>
		/// Gets a reference to a <see cref="Style"/> object that defines the appearance of the instructional text.
		/// </summary>
		/// <remarks>
		/// This property returns a <see cref="Style"/> object that defines the appearance of the control that has an ID of 
		/// <strong>Instruction</strong> in a custom <see cref="Template"/> and the default instructions when a custom template 
		/// is not defined.
		/// </remarks>
		[Category("Styles"), DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		PersistenceMode(PersistenceMode.InnerProperty), NotifyParentProperty(true), PropertyDescription("InstructionTextStyle")]
		public Style InstructionTextStyle
		{
			get
			{
				if (instructionTextStyle == null)
				{
					instructionTextStyle = new Style();

					if (IsTrackingViewState)
						((IStateManager) instructionTextStyle).TrackViewState();
				}

				return instructionTextStyle;
			}
		}

		/// <summary>
		/// Gets a reference to a <see cref="Style"/> object that defines the appearance of the input field's label.
		/// </summary>
		/// <remarks>
		/// This property returns a <see cref="Style"/> object that defines the appearance of the control that has an ID of 
		/// <strong>Label</strong> in a custom <see cref="Template"/> and the default label text when a custom template 
		/// is not defined.
		/// </remarks>
		[Category("Styles"), DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		PersistenceMode(PersistenceMode.InnerProperty), NotifyParentProperty(true), PropertyDescription("LabelTextStyle")]
		public Style LabelTextStyle
		{
			get
			{
				if (labelTextStyle == null)
				{
					labelTextStyle = new Style();

					if (IsTrackingViewState)
						((IStateManager) labelTextStyle).TrackViewState();
				}

				return labelTextStyle;
			}
		}

		/// <summary>
		/// Gets a reference to a <see cref="Style"/> object that defines the appearance of validators' inline error text.
		/// </summary>
		/// <remarks>
		/// This property returns a <see cref="Style"/> object that defines the appearance of the controls that have an ID of 
		/// <strong>RequiredValidator</strong> and <strong>Validator</strong> in a custom <see cref="Template"/> and the default 
		/// validators' text when a custom template is not defined.
		/// </remarks>
		[Category("Styles"), DefaultValue(null), DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		PersistenceMode(PersistenceMode.InnerProperty), NotifyParentProperty(true), PropertyDescription("ValidatorTextStyle")]
		public Style ValidatorTextStyle
		{
			get
			{
				if (validatorTextStyle == null)
				{
					validatorTextStyle = new Style();

					if (IsTrackingViewState)
						((IStateManager) validatorTextStyle).TrackViewState();
				}

				return validatorTextStyle;
			}
		}
		#endregion

		#region Non-browsable
		/// <summary>
		/// Gets or sets the template used to display the <see cref="AutoInputProtectionControl"/> control.
		/// </summary>
		/// <remarks>
		/// <para>
		/// The following table indicates the controls that are <em>required</em> in a custom template: 
		/// </para>
		/// <list type="table">
		///		<listheader>
		///			<term>Control ID</term>
		///			<description>Description</description>
		///		</listheader>
		///		<item>
		///			<term>Image</term>
		///			<description>An <see cref="System.Web.UI.WebControls.Image"/> control.</description>
		///		</item>
		///		<item>
		///			<term>Validator</term>
		///			<description>A <see cref="CustomValidator"/> control.</description>
		///		</item>
		/// </list>
		/// <para>
		/// An <see cref="InvalidOperationException"/> is thrown when a custom template is missing one or more of the required controls.
		/// </para>
		/// <note>
		/// An input control is not actually required since the <see cref="CustomValidator.ServerValidate"/> event is used 
		/// to obtain the text that the user has submitted.  Typically, as in the default template, a <see cref="TextBox"/>
		/// control is assigned an ID of <strong>Text</strong> and the custom validator's <see cref="BaseValidator.ControlToValidate"/>
		/// property is assigned the same value so that when the <see cref="CustomValidator.ServerValidate"/> event is raised, the 
		/// value that the user has entered will be submitted to this control automatically.
		/// </note>
		/// <para>
		/// The following table indicates the controls that are <em>optional</em> in a custom template: 
		/// </para>
		/// <list type="table">
		///		<listheader>
		///			<term>Control ID</term>
		///			<description>Description</description>
		///		</listheader>
		///		<item>
		///			<term>Text</term>
		///			<description>Any control that implements <see cref="ITextControl"/>, such as <see cref="TextBox"/>.  Typically a control
		///			such as <see cref="Label"/>, which also implements <see cref="ITextControl"/>, is not used since user input is required; however, 
		///			in a custom template it may be desirable to use a <see cref="Label"/> and then provide some other means for accepting user input.
		///			</description>
		///		</item>
		///		<item>
		///			<term>Label</term>
		///			<description>Any control that implements <see cref="ITextControl"/>, such as <see cref="Label"/>.</description>
		///		</item>
		///		<item>
		///			<term>Instruction</term>
		///			<description>Any control that implements <see cref="ITextControl"/>, such as <see cref="Label"/>.</description>
		///		</item>
		///		<item>
		///			<term>Title</term>
		///			<description>Any control that implements <see cref="ITextControl"/>, such as <see cref="Label"/>.</description>
		///		</item>
		///		<item>
		///			<term>RequiredValidator</term>
		///			<description>Any control that implements <see cref="IValidator"/>, although typically <see cref="RequiredFieldValidator"/> is used.</description>
		///		</item>
		/// </list>
		/// <para>
		/// No exception is thrown when a custom template is missing an optional control, although the corresponding style and appearance properties will 
		/// no longer be used.  For example, if a custom template does not define a control with an ID of <strong>Title</strong>, then the 
		/// <see cref="TitleTextStyle"/> and <see cref="TitleText"/> properties will be ignored.
		/// </para>
		/// </remarks>
		/// <include file='comments/autoinputprotectioncontrol.xml' path='//comments/example[@id=2]'/>
		/// <value>An <see cref="ITemplate"/> implementation that contains the template for displaying the <see cref="AutoInputProtectionControl"/> control.  
		/// The default value is <see langword="null"/>.</value>
		[Browsable(false), TemplateContainer(typeof(AutoInputProtectionControl)),
		PersistenceMode(PersistenceMode.InnerProperty),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		public virtual ITemplate Template
		{
			get
			{
				return template;
			}
			set
			{
				template = value;
				ChildControlsCreated = false;
			}
		}

		/// <summary>
		/// Gets the text that was entered by the user.
		/// </summary>
		/// <remarks>
		/// <para>
		/// The text submitted by the user is matched against the <see cref="ImageText"/> from the previous request.  If it does not match then
		/// <see cref="IsValid"/> and <see cref="Page.IsValid"/> will be <see langword="false"/>; otherwise, <see cref="IsValid"/> 
		/// will be <see langword="true"/> and <see cref="Page.IsValid"/> will not be effected by this control.
		/// </para>
		/// <para>
		/// See the remarks for the <see cref="AutoInputProtectionControl"/> class for more information.
		/// </para>
		/// </remarks>
		/// <seealso cref="IsValid"/>
		/// <seealso cref="ImageText"/>
		/// <value>The text that was entered by the user.</value>
		[Browsable(false)]
		public string Text
		{
			get
			{
				return TemplateContainer.Text ?? string.Empty;
			}
		}

		/// <summary>
		/// Gets the text from the image that is generated for the current request.
		/// </summary>
		/// <remarks>
		/// <para>
		/// Reading this property will cause AIP to generate a test if one has not already been generated for the current request.
		/// </para>
		/// <note>
		/// The value of this property always represents a new test that has been generated for the current request.  It can never 
		/// be used to retrieve the text for the previous request, to which the <see cref="Text"/> submitted by the user will be 
		/// compared for validation.
		/// </note>
		/// <para>
		/// The image and its text are generated by the default providers that are specified in the configuration file.  See the 
		/// remarks of the <see cref="AutoInputProtectionControl"/> class for more information.
		/// </para>
		/// </remarks>
		/// <seealso cref="IsValid"/>
		/// <seealso cref="Text"/>
		/// <value>The text from the image that is generated for the current request.</value>
		[Browsable(false)]
		public string ImageText
		{
			get
			{
				EnsureImageCreated();

				return generatedText;
			}
		}

		/// <summary>
		/// Gets whether the <see cref="Text"/> submitted matches the <see cref="ImageText"/> for the previous request.
		/// </summary>
		/// <remarks>
		/// <para>
		/// Call <see cref="Validate()"/> or <see cref="Page.Validate()"/> before checking the value of this property.  Alternatively, 
		/// this property may be read in the handler of a control's post back event without explicitly invoking validation.
		/// </para>
		/// <para>
		/// A value of <see langword="true"/> indicates that the <see cref="Text"/> submitted by the user 
		/// matches the <see cref="ImageText"/> that was generated during the previous request.  Since a test may only be used 
		/// once, the previous test is no longer available on the server, regardless of whether the test has succeed or failed.
		/// Therefore, reading the <see cref="ImageText"/> property will return a new test that is generated for the current 
		/// request instead of the previous test that the user's input was validated against.
		/// </para>
		/// </remarks>
		/// <seealso cref="ImageText"/>
		/// <seealso cref="Text"/>
		/// <value>Indicates whether validation succeeded.</value>
		[Browsable(false)]
		public bool IsValid
		{
			get
			{
				return (userValidated) ? validationResult : TemplateContainer.IsValid;
			}
		}
		#endregion
		#endregion

		#region Private / Protected
		/// <summary>
		/// Gets a value of <see cref="HtmlTextWriterTag.Div"/>.
		/// </summary>
		/// <value>The outer HTML element that is rendered by this control.</value>
		protected override HtmlTextWriterTag TagKey
		{
			get
			{
				return HtmlTextWriterTag.Div;
			}
		}

		/// <summary>
		/// Gets the default value for the <see cref="InstructionText"/> property depending upon the value of the 
		/// <see cref="AutoInputProtection.DefaultTextProvider"/>'s 
		/// <see cref="AutoInputProtectionTextProvider.CaseSensitiveComparison">CaseSensitiveComparison</see> property.
		/// </summary>
		/// <value>The default value of the <see cref="InstructionText"/> property.</value>
		private string DefaultInstructionText
		{
			get
			{
				try
				{
					if (AutoInputProtection.DefaultTextProvider.CaseSensitiveComparison)
						return Resources.Text.AIP_InstructionTextCaseSensitive;
					else
						return Resources.Text.AIP_InstructionTextDefault;
				}
				catch (Exception)
				{
					if (DesignMode)
						return Resources.Text.AIP_InstructionTextDefault;
					else
						throw;
				}
			}
		}

		/// <summary>
		/// Gets the <see cref="AutoInputProtectionContainer"/> instance that contains the control's template.
		/// </summary>
		/// <remarks>
		/// The template container will be created when this property is read if <see cref="CreateChildControls"/>
		/// is <see langword="false"/>.
		/// </remarks>
		/// <value>The template's container control.</value>
		private AutoInputProtectionContainer TemplateContainer
		{
			get
			{
				EnsureChildControls();
				return templateContainer;
			}
		}

		/// <summary>
		/// The public key used to identify the image on the server for the previous request.
		/// </summary>
		/// <seealso cref="AutoInputProtection.GeneratePublicCacheKey"/>
		private string validationKey;
		/// <summary>
		/// The text that has been generated for the current request.
		/// </summary>
		/// <seealso cref="ImageText"/>
		private string generatedText;
		/// <summary>
		/// The public key that has been generated for the current request.
		/// </summary>
		/// <seealso cref="AutoInputProtection.GeneratePublicCacheKey"/>
		private string generatedKey;
		private bool imageCreated, userValidated, validationResult;

		private ITemplate template;
		private AutoInputProtectionContainer templateContainer;
		private Style imageStyle, textBoxStyle, titleTextStyle, instructionTextStyle, labelTextStyle, validatorTextStyle;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="AutoInputProtectionControl" /> class.
		/// </summary>
		public AutoInputProtectionControl()
		{
		}
		#endregion

		#region Methods
		/// <summary>
		/// Ensures that a test has been generated for the current request.
		/// </summary>
		/// <remarks>
		/// <para>
		/// This method is called automatically by the <see cref="AutoInputProtectionControl"/> control in <see cref="OnPreRender"/> and whenever
		/// the <see cref="ImageText"/> property is read.
		/// </para>
		/// <para>
		/// Before returning to the caller the <see cref="generatedText"/>, <see cref="generatedKey"/> and <see cref="imageCreated"/> fields 
		/// are assigned and the image is persisted on the server; however, the text is not persisted until <see cref="OnPreRender"/> to avoid
		/// replacing the text from the previous request since it might not have been validated.
		/// </para>
		/// <para>
		/// Calling this method has no effect while in design-mode.
		/// </para>
		/// </remarks>
		/// <seealso cref="GenerateImage"/>
		protected void EnsureImageCreated()
		{
			if (!imageCreated && !DesignMode)
			{
				Size size;

				if (imageStyle == null || imageStyle.IsEmpty)
					size = DefaultImageSize;
				else
					size = new Size((int) imageStyle.Width.Value, (int) imageStyle.Height.Value);

				if (size == Size.Empty)
					size = DefaultImageSize;
				else if (size.Width == 0)
					size = new Size(DefaultImageSize.Width, size.Height);
				else if (size.Height == 0)
					size = new Size(size.Width, DefaultImageSize.Height);

				// It's possible that this method will be called before verification can occur for the current request.  
				// See the ImageText property, for example.
				// To prevent existing image text from being replaced (when UserMode is not None) the text is not cached 
				// by the GenerateAndCacheImage method.  Instead, it's cached by this class's PrepareAIP method, which 
				// is always invoked after verification has occured.

				AutoInputProtectionImage image = AutoInputProtection.GenerateAndCacheImage(
					Context, size, RequestKeepAlive, RequestTimeout, GenerateImage, out generatedKey);

				generatedText = image.Text;

				imageCreated = true;
			}
		}

		/// <summary>
		/// Creates an instance of a class that derives from <see cref="AutoInputProtectionImage"/> using the specified 
		/// <paramref name="size"/>.
		/// </summary>
		/// <remarks>
		/// The default implementation of this method simply returns the result of calling 
		/// <see cref="AutoInputProtection.GenerateRandomAutoInputProtectionImage"/> with the specified <paramref name="size"/>, 
		/// which uses the default providers that are configured to instantiate an <see cref="AutoInputProtectionImage"/> implementation 
		/// that will generate a random image for the current request.
		/// </remarks>
		/// <seealso cref="EnsureImageCreated"/>
		/// <param name="size">The dimensions of the image to be generated.</param>
		/// <returns>An <see cref="AutoInputProtectionImage"/> instance that can generate an image of the specified <paramref name="size"/>.</returns>
		protected virtual AutoInputProtectionImage GenerateImage(Size size)
		{
			return AutoInputProtection.GenerateRandomAutoInputProtectionImage(size);
		}

		/// <summary>
		/// Validates the control.
		/// </summary>
		/// <remarks>
		/// This method may be used to validate the control without validating other controls on the page, although typically
		/// <see cref="Page.Validate()"/> is used instead to validate all controls simultaneously, after which <see cref="Page.IsValid"/>
		/// or <see cref="IsValid"/> may be used to check whether validation succeeded or failed.
		/// </remarks>
		public void Validate()
		{
			TemplateContainer.Validate();
		}

		/// <summary>
		/// Compares the specified <paramref name="text"/> against the <see cref="ImageText"/> of the previous request and 
		/// returns whether they match.
		/// </summary>
		/// <remarks>
		/// Before this method returns to the caller, the test that was generated for the previous request is forcefully expired 
		/// on the server so that neither the <see cref="ImageText"/> or the image itself may be used again.
		/// <para>
		/// After calling this method, <see cref="IsValid"/> may be used externally to determine whether validation has succeeded.
		/// </para>
		/// <para>
		/// The result of this method is cached for the lifetime of the current request so that subsequent calls will always return
		/// the same value, even if the <paramref name="text"/> argument is different than before.
		/// </para>
		/// </remarks>
		/// <seealso cref="Validate(string,string)"/>
		/// <param name="text">The string to be validated.</param>
		/// <returns><see langword="true">True</see> if the specified <paramref name="text"/> matches the <see cref="ImageText"/> of the 
		/// previous request; otherwise, <see langword="false"/>.</returns>
		private bool ValidateUserInputInternal(string text)
		{
			// NOTE: It's possible that server validation may occur multiple times.  The first result should be cached since
			// the text will no longer be available for subsequent validation events.
			// (In testing, the server validation event was raised twice when the AIP web control was being used on a blog page
			// in Community Server.  I cannot reproduce this behavior in a norml web form or user control, so I assume that
			// it's either a bug in CS or the implementation is simply inefficient.  Regardless, this code fixes the issue.)
			if (userValidated)
				return validationResult;

			string imageText = AutoInputProtection.GetCachedTextAndForceExpire(Context, validationKey);
			
#if DEBUG
			if (imageText == null)
			{
				Page.Trace.Write("AIP Validation", "Timeout expired.  User input: " + text);
				System.Diagnostics.Debug.WriteLine("Timeout expired.  User input: " + text, "AIP Validation");
			}
#endif

			validationResult = imageText != null && Validate(text ?? string.Empty, imageText);

			if (validationResult)
				OnUserValidated(EventArgs.Empty);

			userValidated = true;

			return validationResult;
		}

		/// <summary>
		/// Compares the specified <paramref name="text"/> and <paramref name="imageText"/> and returns whether they match.
		/// </summary>
		/// <remarks>
		/// The base implementation of this method simply returns the result of calling 
		/// <see cref="AutoInputProtection.ValidateUserInput"/> with the specified <paramref name="text"/> and <paramref name="imageText"/>, 
		/// which compares the arguments using <see cref="AutoInputProtection.DefaultTextProvider"/>.
		/// </remarks>
		/// <seealso cref="AutoInputProtection.ValidateUserInput"/>
		/// <param name="text">The string to be validated against the <paramref name="imageText"/>.</param>
		/// <param name="imageText">The string that was generated for the image during the previous request.</param>
		/// <returns><see langword="true">True</see> if the arguments match; otherwise, <see langword="false"/>.</returns>
		protected virtual bool Validate(string text, string imageText)
		{
			return AutoInputProtection.ValidateUserInput(text, imageText);
		}

		/// <summary>
		/// Assigns the image control's <see cref="System.Web.UI.WebControls.Image.ImageUrl">ImageUrl</see> property to a URL that uniquely identifies the 
		/// image on the server for the current request.
		/// </summary>
		/// <seealso cref="AutoInputProtection.GetImageUrl"/>
		/// <seealso cref="generatedKey"/>
		private void AssignImageUrl()
		{
			System.Web.UI.WebControls.Image image = (System.Web.UI.WebControls.Image) TemplateContainer.Image;
			
			image.ImageUrl = AutoInputProtection.GetImageUrl(Context, generatedKey);
		}

		/// <summary>
		/// Prepares the control for rendering at runtime by ensuring that the image and text have been generated and cached on 
		/// the server and assigns the image control's <see cref="System.Web.UI.WebControls.Image.ImageUrl">ImageUrl</see> property.
		/// </summary>
		/// <remarks>
		/// This method is invoked in <see cref="OnPreRender"/> to prepare the control to be rendered.
		/// <note>
		/// During a post-back text must be cached <em>after</em> verification occurs since it will replace the existing value.
		/// Therefore, this method must not be called until after verification has occurred.
		/// </note>
		/// </remarks>
		/// <seealso cref="EnsureImageCreated"/>
		/// <seealso cref="AssignImageUrl"/>
		/// <seealso cref="AutoInputProtection.CacheText"/>
		private void PrepareAIP()
		{
			if (!DesignMode)
			{
				EnsureImageCreated();

				AssignImageUrl();

				AutoInputProtection.CacheText(Context, generatedText, generatedKey, ValidationKeepAlive, ValidationTimeout);
			}
		}

		/// <summary>
		/// Creates the <see cref="TemplateContainer"/> and instantiates the user-defined <see cref="Template"/>, or the default 
		/// template, and all child controls.
		/// </summary>
		/// <remarks>
		/// Before this method returns to the caller, the template controls are added to the container, which is added to the 
		/// <see cref="Controls"/> collection and several validator properties are assigned, including the handler for the 
		/// <see cref="CustomValidator.ServerValidate"/> event.
		/// </remarks>
		protected override void CreateChildControls()
		{
			Controls.Clear();

			ITemplate theTemplate = Template;

			if (theTemplate == null)
				theTemplate = new AutoInputProtectionTemplate();

			templateContainer = new AutoInputProtectionContainer(this);

			theTemplate.InstantiateIn(templateContainer);

			Controls.Add(templateContainer);

			// NOTE: Certain validator properties, like ValidationGroup and ErrorMessage, must be assigned now instead of in SetTemplateControlProperties
			// because their values are used during validation, which occurs before the control is rendered.  ValidationGroup is set now so that the ServerValidate 
			// event is handled on post back.  ErrorMessage is set now since its value is used immediately if validation fails.  The other properties haven't
			// been tested individually but just to be safe they are set now as well.

			string validationGroup = ValidationGroup;

			CustomValidator validator = templateContainer.Validator;

			if (validator != null)  // custom validator may be null at design-time only (it's required at runtime)
			{
				validator.ValidateEmptyText = true;
				validator.SetFocusOnError = SetFocusOnFailedValidation;
				validator.ValidationGroup = validationGroup;
				validator.ServerValidate += validator_ServerValidate;

				if (string.IsNullOrEmpty(validator.ErrorMessage))
					validator.ErrorMessage = InvalidErrorMessage;

				if (string.IsNullOrEmpty(validator.Text))
					validator.Text = InvalidErrorText;
			}

			IValidator required = (IValidator) templateContainer.RequiredValidator;

			if (required != null)		// required field validator may be null at design-time and runtime
			{
				if (string.IsNullOrEmpty(required.ErrorMessage))
					required.ErrorMessage = RequiredErrorMessage;

				BaseValidator baseValidator = required as BaseValidator;

				if (baseValidator != null)
				{
					baseValidator.ValidationGroup = validationGroup;

					if (string.IsNullOrEmpty(baseValidator.Text))
						baseValidator.Text = RequiredErrorText;

					baseValidator.ApplyStyle(validatorTextStyle);
				}
			}
		}

		/// <summary>
		/// Causes the control to track changes to its view state so it can be stored in the <see cref="Control.ViewState"/> property.
		/// </summary>
		protected override void TrackViewState()
		{
			base.TrackViewState();

			if (imageStyle != null)
				((IStateManager) imageStyle).TrackViewState();

			if (textBoxStyle != null)
				((IStateManager) textBoxStyle).TrackViewState();

			if (titleTextStyle != null)
				((IStateManager) titleTextStyle).TrackViewState();

			if (instructionTextStyle != null)
				((IStateManager) instructionTextStyle).TrackViewState();

			if (labelTextStyle != null)
				((IStateManager) labelTextStyle).TrackViewState();

			if (validatorTextStyle != null)
				((IStateManager) validatorTextStyle).TrackViewState();
		}

		/// <summary>
		/// Saves any state that was modified after the <see cref="TrackViewState"/> method was invoked.
		/// </summary>
		/// <returns>An object that is the view state for the control.</returns>
		protected override object SaveViewState()
		{
			return new object[] {
				base.SaveViewState(), 
				(imageStyle != null) ? ((IStateManager) imageStyle).SaveViewState() : null,
				(textBoxStyle != null) ? ((IStateManager) textBoxStyle).SaveViewState() : null, 
				(titleTextStyle != null) ? ((IStateManager) titleTextStyle).SaveViewState() : null, 
				(instructionTextStyle != null) ? ((IStateManager) instructionTextStyle).SaveViewState() : null, 
				(labelTextStyle != null) ? ((IStateManager) labelTextStyle).SaveViewState() : null, 
				(validatorTextStyle != null) ? ((IStateManager) validatorTextStyle).SaveViewState() : null
			};
		}

		/// <summary>
		/// Restores internal state that was saved in a previous request by <see cref="SaveViewState"/>.
		/// </summary>
		/// <param name="savedState">An object that is the view state to be loaded into internal control state.</param>
		protected override void LoadViewState(object savedState)
		{
			if (savedState == null)
			{
				base.LoadViewState(null);
				return;
			}

			object[] state = savedState as object[];

			if (state == null || state.Length != 7)
				throw new ArgumentException(Errors.AIPControl_InvalidViewState, "savedState");

			base.LoadViewState(state[0]);

			for (int i = 1; i < state.Length; i++)
			{
				object style = state[i];

				if (style != null)
				{
					switch (i)
					{
						case 1:
							((IStateManager) ImageStyle).LoadViewState(style);
							break;
						case 2:
							((IStateManager) TextBoxStyle).LoadViewState(style);
							break;
						case 3:
							((IStateManager) TitleTextStyle).LoadViewState(style);
							break;
						case 4:
							((IStateManager) InstructionTextStyle).LoadViewState(style);
							break;
						case 5:
							((IStateManager) LabelTextStyle).LoadViewState(style);
							break;
						case 6:
							((IStateManager) ValidatorTextStyle).LoadViewState(style);
							break;
					}
				}
			}
		}

		/// <summary>
		/// Saves control state changes that have occurred since the last post back.
		/// </summary>
		/// <returns>An object this is the control state being saved.</returns>
		protected override object SaveControlState()
		{
			return new Pair(base.SaveControlState(), generatedKey);
		}

		/// <summary>
		/// Restors internal state that was saved in a previous request to <see cref="SaveControlState"/>.
		/// </summary>
		/// <param name="savedState">An object that is the control state to be loaded into the control's internal state.</param>
		protected override void LoadControlState(object savedState)
		{
			if (savedState == null)
			{
				base.LoadControlState(null);
				return;
			}

			Pair state = savedState as Pair;

			if (state == null)
				throw new ArgumentException(Errors.AIPControl_InvalidControlState, "savedState");

			base.LoadControlState(state.First);

			validationKey = (string) state.Second;
		}

		/// <summary>
		/// Writes the control's content to the specified <paramref name="writer"/> for display on the client.
		/// </summary>
		/// <param name="writer"><see cref="HtmlTextWriter"/> to which the control's content is written.</param>
		protected override void Render(HtmlTextWriter writer)
		{
			if (this.Page != null)
				this.Page.VerifyRenderingInServerForm(this);
			
			if (base.DesignMode)
			{
				ChildControlsCreated = false;
				EnsureChildControls();
			}

			SetTemplateControlProperties();

			base.Render(writer);
		}

		/// <summary>
		/// Assigns child control properties that correspond to properties of this class, such as 
		/// <see cref="TitleText"/> and <see cref="ValidatorTextStyle"/>, for example.
		/// </summary>
		private void SetTemplateControlProperties()
		{
			SetTextAndStyle((ITextControl) templateContainer.Title, TitleText, true, titleTextStyle);
			SetTextAndStyle((ITextControl) templateContainer.Instruction, InstructionText, true, instructionTextStyle);
			SetTextAndStyle((ITextControl) templateContainer.Label, LabelText, true, labelTextStyle);
			SetTextAndStyle((ITextControl) templateContainer.TextBox, string.Empty, false, textBoxStyle);

			WebControl image = templateContainer.Image as WebControl;

			if (image != null)  // image may be null at design-time only
				image.ApplyStyle(imageStyle);

			CustomValidator validator = templateContainer.Validator;

			if (validator != null)  // validator may be null at design-time only
				validator.ApplyStyle(validatorTextStyle);
		}

		/// <summary>
		/// Sets the <see cref="ITextControl.Text">Text</see> property of the specified <paramref name="textControl"/> to the specified
		/// <paramref name="text"/> and also applies the specified <paramref name="style"/>.
		/// </summary>
		/// <remarks>
		/// The <paramref name="hideWhenTextIsEmpty"/> and <paramref name="style"/> arguments are ignored unless the specified 
		/// <paramref name="textControl"/> derives from <see cref="WebControl"/>.
		/// </remarks>
		/// <param name="textControl">An <see cref="ITextControl"/> implementation to which the specified <paramref name="text"/> will be assigned.</param>
		/// <param name="text">The string that will be assigned to the specified <paramref name="textControl"/>.</param>
		/// <param name="hideWhenTextIsEmpty">Indicates whether the <paramref name="textControl"/> should be hidden if the specified <paramref name="text"/>
		/// is an empty string.</param>
		/// <param name="style">The <see cref="Style"/> to be applied to the <paramref name="textControl"/>.</param>
		private static void SetTextAndStyle(ITextControl textControl, string text, bool hideWhenTextIsEmpty, Style style)
		{
			if (textControl != null)
			{
				WebControl control = textControl as WebControl;

				if (!hideWhenTextIsEmpty || text.Length > 0)
				{
					textControl.Text = text;

					if (style != null)
					{
						if (control != null)
							control.ApplyStyle(style);
					}

					if (control != null)
						control.Visible = true;
				}
				else if (control != null)
					control.Visible = false;
			}
		}
		#endregion

		#region Events
		private readonly object UserValidatedEvent = new object();

		/// <summary>
		/// Raised when the <see cref="Text" /> submitted by the user matches the <see cref="ImageText"/> for the previous request.
		/// </summary>
		/// <remarks>
		/// This event is raised by <see cref="ValidateUserInputInternal"/> during the custom validator control's 
		/// <see cref="CustomValidator.ServerValidate">ServerValidate</see> event when the <see cref="Validate(string,string)"/> method 
		/// returns <see langword="true"/>.
		/// </remarks>
		[Category("Validation")]
		[Description("Raised when the text submitted by the user matches the text on the image for the previous request.")]
		public event EventHandler UserValidated
		{
			add
			{
				lock (UserValidatedEvent)
				{
					Events.AddHandler(UserValidatedEvent, value);
				}
			}
			remove
			{
				lock (UserValidatedEvent)
				{
					Events.RemoveHandler(UserValidatedEvent, value);
				}
			}
		}

		/// <summary>
		/// Raises the <see cref="UserValidated" /> event.
		/// </summary>
		/// <param name="e"><see cref="EventArgs" /> object that provides the arguments for the event.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
		protected virtual void OnUserValidated(EventArgs e)
		{
			EventHandler handler = null;

			lock (UserValidatedEvent)
			{
				handler = (EventHandler) Events[UserValidatedEvent];
			}

			if (handler != null)
				handler(this, e);
		}
		#endregion

		#region Event Handlers
		/// <summary>
		/// Ensures that child controls are created by calling <see cref="Control.EnsureChildControls"/> and raises the <see cref="Control.Init"/> event.
		/// </summary>
		/// <param name="e"><see cref="EventArgs"/> object that provides the arguments for the event.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
		protected override void OnInit(EventArgs e)
		{
			// Controls must be created now to ensure that validator_ServerValidate is called when Page.Validate is called, even if the request 
			// is not a post back.
			// 
			// When EnsureChildControls is not called here, Page.IsValid will not be affected at all by the custom validator upon the first request 
			// to a page that contains the AIP web control; nor will AIP have an effect on Page.IsValid during a post back if its controls aren't 
			// created before Page validation occurs.
			//
			// The issue is not with validators, per se, but with templated controls.  This behavior can be reproduced using an FCL control: 
			// 
			// 1. Create a new web form.
			// 2. Add a Page.Load event handler that simply calls Page.Validate()
			// 3. Add a CustomValidator control to the page.
			// 4. Add a ServerValidate event handler that renders something to indicate that it was called (e.g., enable page tracing and use Trace.Write)
			// 5. Browse to the page (wihtout causing a post back) and you'll see that the ServerValidate event is called.
			// 6. Add a Login control to the same page.
			// 7. Define a custom LayoutTemplate.  (Note that you'll have to add TextBox controls with ID="UserName" and ID="Password" to satisfy the control's requirements.)
			// 8. Move the CustomValidator control into the template.
			// 9. Browse to the page again (without causing a post back) and you'll see that the ServerValidate event is _not_ called.
			// 10. In the Page.Load event handler, before Page.Validate() is called, add code that ensures the login control's child control collection is created.
			//     For example, you could assign a variable to the Controls property (the variable doesn't have to actually be used):   object c = login1.Controls
			// 11. Browse to the page again (without causing a post back) and you'll see that the ServerValidate event is called.
			//
			EnsureChildControls();

			base.OnInit(e);
		}

		/// <summary>
		/// Registers this control as one that requires control state persistence and raises the <see cref="Control.Load"/> event.
		/// </summary>
		/// <param name="e"><see cref="EventArgs"/> object that provides the arguments for the event.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
		protected override void OnLoad(EventArgs e)
		{
			Page.RegisterRequiresControlState(this);

			base.OnLoad(e);
		}

		/// <summary>
		/// Prepares the control for rendering by calling <see cref="PrepareAIP"/> and raises the <see cref="Control.PreRender"/> event.
		/// </summary>
		/// <param name="e"><see cref="EventArgs"/> object that provides the arguments for the event.</param>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
		protected override void OnPreRender(EventArgs e)
		{
			PrepareAIP();
			
			base.OnPreRender(e);
		}

		/// <summary>
		/// Handles the custom validator control's <see cref="CustomValidator.ServerValidate">ServerValidate</see> event.
		/// </summary>
		/// <remarks>
		/// This method assigns the <see cref="ServerValidateEventArgs.IsValid">IsValid</see> property of the specified 
		/// <paramref name="args"/> to the return value of the <see cref="ValidateUserInputInternal"/> method, supplying 
		/// the <see cref="ServerValidateEventArgs.Value">Value</see> of <paramref name="args"/> as input.
		/// </remarks>
		/// <param name="source">Object that raised the event.</param>
		/// <param name="args"><see cref="ServerValidateEventArgs"/> object that provides arguments for the event.</param>
		private void validator_ServerValidate(object source, ServerValidateEventArgs args)
		{
			args.IsValid = ValidateUserInputInternal(args.Value);
		}
		#endregion

		#region Nested
		[ToolboxItem(false)]
		private sealed class AutoInputProtectionContainer : WebControl
		{
			#region Public Properties
			public Control Title
			{
				get
				{
					if (title == null)
						title = FindControl<ITextControl>(TitleId, false);

					return title;
				}
				set
				{
					title = value;
				}
			}

			public Control Instruction
			{
				get
				{
					if (instruction == null)
						instruction = FindControl<ITextControl>(InstructionId, false);

					return instruction;
				}
				set
				{
					instruction = value;
				}
			}

			public Control Label
			{
				get
				{
					if (label == null)
						label = FindControl<ITextControl>(LabelId, false);

					return label;
				}
				set
				{
					label = value;
				}
			}

			public Control TextBox
			{
				get
				{
					if (textBox == null)
						// NOTE: The text box is not actually required - only a custom validator is required and it
						// can work on any control declared in a custom template; although, the default template uses 
						// a text box.
						textBox = FindControl<ITextControl>(TextBoxId, false);

					return textBox;
				}
				set
				{
					textBox = value;
				}
			}

			public Control Image
			{
				get
				{
					if (image == null)
						image = FindControl<System.Web.UI.WebControls.Image>(ImageId, true);

					return image;
				}
				set
				{
					image = value;
				}
			}

			public Control RequiredValidator
			{
				get
				{
					if (requiredValidator == null)
						requiredValidator = FindControl<IValidator>(RequiredValidatorId, false);

					return requiredValidator;
				}
				set
				{
					requiredValidator = value;
				}
			}

			public CustomValidator Validator
			{
				get
				{
					if (validator == null)
						validator = (CustomValidator) FindControl<CustomValidator>(ValidatorId, true);

					return validator;
				}
				set
				{
					validator = value;
				}
			}

			public bool IsValid
			{
				get
				{
					return validator.IsValid && (requiredValidator == null || ((IValidator) requiredValidator).IsValid);
				}
			}

			public string Text
			{
				get
				{
					ITextControl textControl = (ITextControl) TextBox;

					return (textControl == null) ? null : textControl.Text;
				}
			}
			#endregion

			#region Private / Protected
			private Control textBox, image;
			private CustomValidator validator;
			private Control requiredValidator;
			private Control title, instruction, label;
			private readonly AutoInputProtectionControl owner;
			#endregion

			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="AutoInputProtectionContainer" /> class.
			/// </summary>
			public AutoInputProtectionContainer(AutoInputProtectionControl owner)
			{
				this.owner = owner;
			}
			#endregion

			#region Methods
			public void Validate()
			{
				if (requiredValidator != null)
					((IValidator) requiredValidator).Validate();

				validator.Validate();
			}

			private Control FindControl<T>(string id, bool required)
			{
				Control control = FindControl(id);

				if (control is T)
					return control;
				else if (required && !owner.DesignMode)
					throw new InvalidOperationException(string.Format(System.Globalization.CultureInfo.CurrentCulture,
						Resources.Errors.AIPControl_MissingControlInTemplate, Environment.NewLine, id, typeof(T).FullName));
				else
					return null;
			}

			protected override void Render(HtmlTextWriter writer)
			{
				RenderContents(writer);
			}
			#endregion
		}

		private sealed class AutoInputProtectionTemplate : ITemplate
		{
			#region Private / Protected
			#endregion

			#region Constructors
			/// <summary>
			/// Constructs a new instance of the <see cref="AutoInputProtectionTemplate" /> class.
			/// </summary>
			public AutoInputProtectionTemplate()
			{
			}
			#endregion

			#region Methods
			private static void CreateControls(AutoInputProtectionContainer container)
			{
				container.Title = new DivLabel { ID = TitleId };
				container.Instruction = new DivLabel { ID = InstructionId };
				container.Label = new Label { ID = LabelId, AssociatedControlID = TextBoxId };
				container.TextBox = new TextBox { ID = TextBoxId, EnableViewState = false };
				container.Image = new System.Web.UI.WebControls.Image
				{
					ID = ImageId,
					Width = Unit.Pixel(DefaultImageSize.Width),
					Height = Unit.Pixel(DefaultImageSize.Height)
				};

				container.RequiredValidator = new RequiredFieldValidator
				{
					ID = RequiredValidatorId,
					ControlToValidate = TextBoxId,
					Display = ValidatorDisplay.Dynamic
				};

				container.Validator = new CustomValidator
				{
					ID = ValidatorId,
					ControlToValidate = TextBoxId,
					Display = ValidatorDisplay.Dynamic
				};
			}

			private static void LayoutControls(AutoInputProtectionContainer container)
			{
				container.Controls.Add(container.Title);
				container.Controls.Add(container.Image);
				container.Controls.Add(container.Instruction);

				container.Controls.Add(container.Label);
				container.Controls.Add(container.TextBox);
				container.Controls.Add(container.RequiredValidator);
				container.Controls.Add(container.Validator);
			}
			#endregion

			#region ITemplate Members
			public void InstantiateIn(Control container)
			{
				AutoInputProtectionContainer c = (AutoInputProtectionContainer) container;
				CreateControls(c);
				LayoutControls(c);
			}
			#endregion

			#region Nested
			[ToolboxItem(false)]
			private sealed class DivLabel : Label
			{
				protected override HtmlTextWriterTag TagKey
				{
					get
					{
						return HtmlTextWriterTag.Div;
					}
				}
			}
			#endregion
		}
		#endregion
	}
}
